local L = LibStub("AceLocale-3.0"):GetLocale("AzCastBarOptions")
local _G = getfenv(0);
local GetTime = GetTime;
local FormatTime = AzCastBar.FormatTime;
local UnitAura = UnitAura;

-- Variables
local b = AzCastBar.CreateBar("Button");
local timers = {};
local frames = {};
local unit = "player";
local wpnImbuePattern = "(.+) %(%d+ .+%)";

-- Extra Options
local extraOptions = {
	{
		[0] = L["Additional"],
		{ type = "Check", var = "showBuffs", default = true, label = L["Show Buffs"] },
		{ type = "Check", var = "showDebuffs", default = true, label = L["Show Debuffs"] },
		{ type = "Check", var = "showEnchants", default = true, label = L["Show Temporary Weapon Enchants"] },
		{ type = "Check", var = "showTracking", default = false, label = L["Show Current Tracking"], y = 16 },
		{ type = "Color", var = "colBuff", default = { 0.4, 0.6, 0.8, 1 }, label = L["Buff Color"] },
		{ type = "Color", var = "colDebuff", default = { 1.0, 0.5, 0.5, 1 }, label = L["Debuff Color"] },
		{ type = "Color", var = "colTimeOut", default = { 0.8, 0.8, 0.2, 1 }, label = L["Buff Timeout Color"] },
		{ type = "Color", var = "colEnchant", default = { 0.45, 0.82, 0.55, 1 }, label = L["Temporary Weapon Enchant Color"] },
	},
};

--------------------------------------------------------------------------------------------------------
--                                             Bar Scripts                                            --
--------------------------------------------------------------------------------------------------------

-- OnUpdate
local function OnUpdate(self,elapsed)
	local timer = self.timer;
	self.timeLeft = max(0,timer.endTime - GetTime());
	if (timer.enchant) then
		self.status:SetValue(1);
		self.time:SetText(FormatTime(self.timeLeft));
	else
		self.status:SetValue(timer.duration == 0 and 0 or self.timeLeft);
		self.time:SetText(timer.duration == 0 and "" or (FormatTime(self.timeLeft)..(b.cfg.showTotalTime and " / "..FormatTime(timer.duration) or "")));
		if (not timer.debuff) and (not timer.enchant) and (timer.duration > 0) and (self.timeLeft <= 30) and (not self.colorChanged) then
			self.status:SetStatusBarColor(unpack(b.cfg.colTimeOut));
			self.colorChanged = 1;
		end
	end
end

-- OnClick
local function OnClick(self,button)
	local timer = self.timer;
	if (timer.enchant) then
		CancelItemTempEnchantment(timer.enchant - 15);
	elseif (timer.tracking) then
		ToggleDropDownMenu(1,nil,MiniMapTrackingDropDown,"cursor",0,-5);
--		SetTracking();
	else
		CancelUnitBuff(unit,timer.index,timer.debuff and "HARMFUL" or "HELPFUL");
	end
end

-- OnEnter
local function OnEnter(self)
	local timer = self.timer;
	GameTooltip:SetOwner(self,"ANCHOR_RIGHT");
	if (timer.enchant) then
		GameTooltip:SetInventoryItem("player",timer.enchant);
	elseif (timer.tracking) then
		GameTooltip:SetTracking();
	else
		GameTooltip:SetUnitAura(unit,timer.index,timer.debuff and "HARMFUL" or "HELPFUL");
	end
end

-- OnLeave
local function HideGTT()
	GameTooltip:Hide();
end

--------------------------------------------------------------------------------------------------------
--                                                Code                                                --
--------------------------------------------------------------------------------------------------------

-- Configure Bar
local function ConfigureBar(bar,id)
	bar:EnableMouse(1);
	bar:RegisterForClicks("RightButtonUp");
	bar:SetScript("OnUpdate",OnUpdate);
	bar:SetScript("OnClick",OnClick);
	bar:SetScript("OnEnter",OnEnter);
	bar:SetScript("OnLeave",HideGTT);
	tinsert(b.bars,bar);
	AzCastBar.ApplyBarSettingsSpecific(b,id);
	return bar;
end

-- Update Timers
local function UpdateTimers()
	-- Loop Timers and update bars
	local bar;
	for index, table in ipairs(timers) do
		bar = b.bars[index] or ConfigureBar(AzCastBar.CreateBar("Button"),index);
		bar.timer = table;
		bar.status:SetMinMaxValues(0,table.enchant and 1 or table.duration == 0 and 1 or table.duration);
		bar.status:SetStatusBarColor(unpack(table.enchant and b.cfg.colEnchant or table.debuff and b.cfg.colDebuff or b.cfg.colBuff));
		bar.colorChanged = nil;
		bar.name:SetText(table.label..(table.count and table.count > 1 and " ("..table.count..")" or ""));
		bar.icon:SetTexture(table.icon);
		bar:Show();
	end
	-- Hide all other frames
	for i = #timers + 1, #b.bars do
		b.bars[i]:Hide();
	end
end

-- Sort the Buffs
local function SortBuffsFunc(a,b)
	-- Enchants
	if (a.enchant or b.enchant)then
		if (a.enchant and b.enchant) then
			return a.enchant < b.enchant;
		else
			return (a.enchant or 0) > (b.enchant or 0);
		end
	-- If both is the same buff type
	elseif (a.debuff == b.debuff)then
		if (a.duration == 0) or (b.duration == 0) then
			if (a.duration == b.duration) then
				return a.label < b.label;
			else
				return a.duration < b.duration;
			end
		else
			return a.endTime > b.endTime;
		end
	-- Different Buff Types
	else
		return tostring(a.debuff) > tostring(b.debuff);
	end
end

-- Scan Weapon for Temp Enchant
local function ScanWeaponTipEnchant(slotId)
	AzCastBarTip:ClearLines();
	AzCastBarTip:SetInventoryItem("player",slotId);
	local enchant;
	for i = 6, AzCastBarTip:NumLines() do
		enchant = _G["AzCastBarTipTextLeft"..i]:GetText():match(wpnImbuePattern);
		if (enchant) then
			return enchant;
		end
	end
	return "??";
end

-- Query Buffs
local index;
local name, rank, icon, count, debuffType, duration, expirationTime;
local function QueryAuras(auraType)
	index = 1;
	while (1) do
		name, rank, icon, count, debuffType, duration, expirationTime = UnitAura(unit,index,auraType);
		if (not name) then
			break;
		end
		tinsert(timers,{ index = index, label = name, icon = icon, count = count, debuffType = debuffType, duration = duration, endTime = expirationTime, debuff = (auraType == "HARMFUL" and 1) });
		index = (index + 1);
	end
end

-- Scan Player Buffs
local function ScanPlayerBuffs()
	wipe(timers);
	-- Weapon Enchants
	if (b.cfg.showEnchants) then
		local endTime;
		local hasMainHandEnchant, mainHandExpiration, _, hasOffHandEnchant, offHandExpiration = GetWeaponEnchantInfo();
		if (hasMainHandEnchant) then
			endTime = GetTime() + (mainHandExpiration / 1000);
			tinsert(timers,{ label = ScanWeaponTipEnchant(16), icon = GetInventoryItemTexture(unit,16), duration = 0, endTime = endTime, enchant = 16 });
		end
		if (hasOffHandEnchant) then
			endTime = GetTime() + (offHandExpiration / 1000);
			tinsert(timers,{ label = ScanWeaponTipEnchant(17), icon = GetInventoryItemTexture(unit,17), duration = 0, endTime = endTime, enchant = 17 });
		end
	end
	-- Tracking
	if (b.cfg.showTracking) then
		local name, texture, active, category;
		for i = 1, GetNumTrackingTypes() do
			name, texture, active, category = GetTrackingInfo(i);
			if (active) then
				tinsert(timers,{ label = name, icon = texture, duration = 0, endTime = 0, tracking = 1 });
				break;
			end
		end
	end
	-- Query Auras
	if (b.cfg.showBuffs) then
		QueryAuras("HELPFUL");
	end
	if (b.cfg.showDebuffs) then
		QueryAuras("HARMFUL");
	end
	-- Sort & Update
	sort(timers,SortBuffsFunc);
	UpdateTimers();
end

--------------------------------------------------------------------------------------------------------
--                                           Event Handling                                           --
--------------------------------------------------------------------------------------------------------

-- There is no events for temp weapon enchants, UNIT_INVENTORY_CHANGED seems to be the best way to determine when you gain or lose one.
-- The Blizz UI uses an OnUpdate handler to query GetWeaponEnchantInfo() on each update, I don't like that method at all.
local function OnEvent(self,event,p1,...)
	if (event ~= "UNIT_AURA" or p1 == unit) then
		ScanPlayerBuffs();
	end
end

-- OnConfigChanged -- Enable/Disable Plugin & Blizz Buff Frames
function b:OnConfigChanged(cfg,barId)
	if (barId ~= 1) then
		return;
	elseif (cfg.enabled) then
		BuffFrame:UnregisterAllEvents();
		BuffFrame:Hide();
		TemporaryEnchantFrame:Hide();
		self:RegisterEvent("UNIT_AURA");
		self:RegisterEvent("MINIMAP_UPDATE_TRACKING");
		self:RegisterEvent("UNIT_INVENTORY_CHANGED");
		ScanPlayerBuffs();
	else
		BuffFrame:RegisterEvent("UNIT_AURA");
		BuffFrame:Show();
		if (BuffFrame:GetScript("OnEvent")) then
			BuffFrame:GetScript("OnEvent")(BuffFrame,"UNIT_AURA",unit);
		end
		TemporaryEnchantFrame:Show();
		self:UnregisterAllEvents();
		wipe(timers);
		UpdateTimers();
	end
end

--------------------------------------------------------------------------------------------------------
--                                        Init AzCastBar Entry                                        --
--------------------------------------------------------------------------------------------------------

b.token = L["PlayerAuras"];
b.options = extraOptions;
b.bars = {};
b:ClearAllPoints();
b:SetPoint("CENTER");
b:SetScript("OnEvent",OnEvent);
tinsert(AzCastBar.frames,b);
ConfigureBar(b,1);